package xyz.hubery.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CreateCache;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import xyz.hubery.domain.*;
import xyz.hubery.dto.UserBankDTO;
import xyz.hubery.dto.UserDTO;
import xyz.hubery.feign.AdminServiceFeign;
import xyz.hubery.feign.UserBankServiceFeign;
import xyz.hubery.feign.UserFeign;
import xyz.hubery.mapper.CashWithdrawalsMapper;
import xyz.hubery.model.SellGCNParam;
import xyz.hubery.service.*;

@Service
public class CashWithdrawalsServiceImpl extends ServiceImpl<CashWithdrawalsMapper, CashWithdrawals> implements CashWithdrawalsService {

    @Autowired
    private UserFeign userFeign;
    @Autowired
    private UserBankServiceFeign userBankServiceFeign;

    @Autowired
    private ConfigService configService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private AccountService accountService;
    @Autowired
    private CoinService coinService;
    @CreateCache(name = "WITH_DRAW_LOCK:", expire = 100, timeUnit = TimeUnit.SECONDS)
    private Cache<String, String> cache;
    @Autowired
    private AdminServiceFeign adminServiceFeign;
    @Autowired
    private CashWithdrawAuditRecordService cashWithdrawAuditRecordService;


    @Override
    public Page<CashWithdrawals> findPage(Page<CashWithdrawals> page, Long userId, String userName, String mobile, Byte status, Integer numMin, Integer numMax, String startTime, String endTime) {
        page.addOrder(OrderItem.desc("last_update_time"));

        // 构造分页查询条件
        LambdaQueryWrapper<CashWithdrawals> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(userId != null, CashWithdrawals::getUserId, userId);
        wrapper.eq(status != null, CashWithdrawals::getStatus, status);
        wrapper.between(numMin != null && numMax != null, CashWithdrawals::getNum, numMin, numMax);
        wrapper.between(startTime != null && endTime != null, CashWithdrawals::getCreated, startTime, endTime);

        Page<CashWithdrawals> cashWithdrawalsPage = page(page, wrapper);
        List<Long> userIds = new ArrayList<>();
        // 获取用户id列表
        if (userId != null) {  // 直接根据userId查询
            userIds.add(userId);
        } else { // 提取用户id列表
            userIds = cashWithdrawalsPage.getRecords().stream().map(CashWithdrawals::getUserId).distinct().collect(Collectors.toList());
        }
        if (CollectionUtils.isEmpty(userIds)) {
            return cashWithdrawalsPage;
        }
        // 远程调用根据用户id列表查询用户信息
        List<UserDTO> userDTOList = userFeign.getBasicUserInfo(userIds);
        // 转换为map结构
        Map<Long, UserDTO> userDTOMap = userDTOList.stream().collect(Collectors.toMap(UserDTO::getId, user -> user));
        // 提取出符合条件的充值记录
        List<CashWithdrawals> filteredRecords = new ArrayList<>();
        Set<Long> filterIds = new HashSet<Long>();
        for (UserDTO userDTO : userDTOList) {
            boolean match = true;
            if (!StringUtils.isEmpty(userName) && !userDTO.getUsername().contains(userName)) { //根据用户名进行匹配
                match = false;
            }
            if (!StringUtils.isEmpty(mobile) && !userDTO.getMobile().contains(mobile)) { //根据手机号进行匹配
                match = false;
            }
            if (match) {
                filterIds.add(userDTO.getId());
            }
        }

        List<CashWithdrawals> records = cashWithdrawalsPage.getRecords();
        for (CashWithdrawals record : records) {
            if (filterIds.contains(record.getUserId())) {
                // 填充用户信息
                UserDTO userDTO = userDTOMap.get(record.getUserId());
                record.setUsername(userDTO.getUsername());
                filteredRecords.add(record);
            }
        }

        // 替换为过滤后的记录
        cashWithdrawalsPage.setRecords(filteredRecords);
        cashWithdrawalsPage.setTotal(filteredRecords.size());
        return cashWithdrawalsPage;

    }

    @Override
    public Page<CashWithdrawals> findCashWithdrawalsPage(Page<CashWithdrawals> page, Long userId, Byte status) {
        return page(page, new LambdaQueryWrapper<CashWithdrawals>()
                .eq(CashWithdrawals::getUserId, userId)
                .eq(status != null, CashWithdrawals::getStatus, status));
    }

    @Transactional
    @Override
    public boolean sell(Long userId, SellGCNParam param) {
        // 1、校验GCN卖出参数
        checkSellParam(param);
        // 2、查询用户信息
        List<UserDTO> userDTOList = userFeign.getBasicUserInfo(Collections.singletonList(userId));
        if (CollectionUtils.isEmpty(userDTOList)) {
            throw new IllegalArgumentException("用户不存在");
        }
        UserDTO user = userDTOList.get(0);
        // 2、比对手机验证码
        checkPhoneCode(user.getMobile(), param.getValidateCode());
        // 3、比对支付密码
        checkPayPassword(user.getPaypassword(), param.getPayPassword());
        // 4、创建订单
        CashWithdrawals cashWithdrawals = new CashWithdrawals();
        // 查询用户银行卡
        UserBankDTO userBank = userBankServiceFeign.getCurrentUserInfo();
        cashWithdrawals.setBankAddr(userBank.getBankAddr());//bank_addr
        cashWithdrawals.setTruename(userBank.getRealName());//truename
        cashWithdrawals.setBankProv(userBank.getBankProv());//bank_prov
        cashWithdrawals.setBank(userBank.getBank());//bank
        cashWithdrawals.setBankCard(userBank.getBankCard());//bank_card
        cashWithdrawals.setBankCity(userBank.getBankCity());//bank_city

        // 查询提现手续费
        BigDecimal fee = getWithDrawalsFee(param.getNum());
        cashWithdrawals.setMum(param.getNum().subtract(fee));//mum:实际提现金额
        cashWithdrawals.setFee(fee);//fee
        cashWithdrawals.setNum(param.getNum());//num

        // 查询用户账户
        Coin coin = coinService.getById(param.getCoinId());
        if (coin == null) {
            throw new IllegalArgumentException("币种不存在");
        }
        Account account = accountService.getUserAccountByCoinName(userId, coin.getName());
        if (account == null) {
            throw new IllegalArgumentException("用户账户不存在");
        }
        cashWithdrawals.setAccountId(account.getId());//account_id
        cashWithdrawals.setStep((byte) 1);//step
        cashWithdrawals.setStatus((byte) 0);//status
        cashWithdrawals.setUserId(userId);//user_id
        cashWithdrawals.setCoinId(param.getCoinId());//coin_id

        boolean save = save(cashWithdrawals);
        if (save) {
            // 5、如果订单创建成功，账号余额扣减
            // 扣减总资产--account-->accountDetail
            accountService.lockUserAmount(userId, cashWithdrawals.getCoinId(),
                    cashWithdrawals.getMum(), "withdrawals_out", cashWithdrawals.getId(),
                    cashWithdrawals.getFee());
        }

        return save;
    }

    /**
     * 计算提现手续费
     *
     * @param num 提现数量
     * @return
     */
    private BigDecimal getWithDrawalsFee(BigDecimal num) {
        // 最小取现手续费
        Config minPoundageConfig = configService.getConfigByCode("WITHDRAW_MIN_POUNDAGE");
        BigDecimal fee1 = new BigDecimal(minPoundageConfig.getValue());  // 使用方式一时的手续费

        // 取现手续费率
        Config poundageRateConfig = configService.getConfigByCode("WITHDRAW_POUNDAGE_RATE");
        BigDecimal fee2 = num.multiply(new BigDecimal(poundageRateConfig.getValue())); //方式二的手续费

        return fee1.compareTo(fee2) > 0 ? fee1 : fee2;
    }

    /**
     * 校验支付密码
     *
     * @param paypasswordDB 用户支付密码
     * @param payPassword   前端传入的支付密码
     */
    private void checkPayPassword(String paypasswordDB, String payPassword) {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        if (!encoder.matches(payPassword, paypasswordDB)) {
            throw new IllegalArgumentException("支付密码错误");
        }
    }

    /**
     * 校验手机验证码
     *
     * @param mobile       手机号
     * @param validateCode 验证码
     */
    private void checkPhoneCode(String mobile, String validateCode) {
        String code = redisTemplate.opsForValue().get("SMS:" + mobile);
        if (!validateCode.equals(code)) {
            throw new IllegalArgumentException("手机验证码错误");
        }
        // 匹配成功，删除缓存
        redisTemplate.delete("SMS:" + mobile);
    }

    /**
     * 校验GCN卖出参数
     *
     * @param param
     */
    private void checkSellParam(SellGCNParam param) {
        // 首先查询当前提现状态
        Config withdrawStatusConfig = configService.getConfigByCode("WITHDRAW_STATUS");
        if (withdrawStatusConfig == null || !"1".equals(withdrawStatusConfig.getValue())) {
            throw new IllegalArgumentException("当前提现功能不可用");
        }
        // 查询最小提现数量
        Config minWithDrawConfig = configService.getConfigByCode("WITHDRAW_MIN_AMOUNT");
        // 单词最大提现数量
        Config maxWithDrawConfig = configService.getConfigByCode("WITHDRAW_MAX_AMOUNT");
        BigDecimal num = param.getNum();

        if (num.compareTo(new BigDecimal(minWithDrawConfig.getValue())) < 0) {
            throw new IllegalArgumentException("提现数量不能小于" + minWithDrawConfig.getValue());
        }
        if (num.compareTo(new BigDecimal(maxWithDrawConfig.getValue())) > 0) {
            throw new IllegalArgumentException("提现数量不能大于" + maxWithDrawConfig.getValue());
        }

    }

    @Transactional
    @Override
    public boolean updateStatus(Long adminId, CashWithdrawAuditRecord cashWithdrawAuditRecord) {
        boolean b = cache.tryLockAndRun(cashWithdrawAuditRecord.getId() + "", 100, TimeUnit.MINUTES, () -> {
            Long id = cashWithdrawAuditRecord.getId();
            CashWithdrawals cashWithdrawals = getById(id);
            if (cashWithdrawals == null) {
                throw new IllegalArgumentException("提现记录不存在");
            }
            if (cashWithdrawals.getStatus() != 0) {
                throw new IllegalArgumentException("该提现记录已审核");
            }
            // 1、新增一条提现审核记录
            CashWithdrawAuditRecord record = new CashWithdrawAuditRecord();
            record.setOrderId(id);
            record.setStatus(cashWithdrawAuditRecord.getStatus());
            record.setRemark(cashWithdrawals.getRemark());
            record.setStep((byte) (cashWithdrawals.getStep() + 1));
            record.setAuditUserId(adminId);
            record.setAuditUserName(adminServiceFeign.getAdminName(adminId));
            boolean save = cashWithdrawAuditRecordService.save(record);
            if (save) {
                // 2、更新提现记录信息
                cashWithdrawals.setRemark(cashWithdrawAuditRecord.getRemark());
                cashWithdrawals.setStep((byte) (cashWithdrawals.getStep() + 1));
                cashWithdrawals.setStatus(cashWithdrawAuditRecord.getStatus());
                cashWithdrawals.setLastTime(new Date());
                updateById(cashWithdrawals);
                // 3、若审核通过，修改账户余额
                if(cashWithdrawals.getStatus() == 1) {
                    boolean isOk = accountService.subAmount(adminId, cashWithdrawals.getUserId(), cashWithdrawals.getCoinId(), cashWithdrawals.getId(),cashWithdrawals.getMum(), cashWithdrawals.getFee());
                }
            }

        });


        return b;
    }
}
